package com.theblind.agent;


import com.theblind.utils.StringUtil;

import java.time.Duration;
import java.time.Instant;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;


/**
 *
 * InheritableThreadLocal 可完成父子线程的数据共享
 * @author TheBlind
 */
public class MonitoringCache {

    private static final ConcurrentHashMap<String, MonitoringData> MONITORING_DATA_CACHE = new ConcurrentHashMap<String, MonitoringData>();

    private static List<MonitoringDataTree> MonitoringDataCacheTree;

    /**
     * temporaryStack 的下标
     */
    private static final ThreadLocal<AtomicInteger> INDEX = new ThreadLocal<AtomicInteger>() {
        @Override
        protected AtomicInteger initialValue() {
            return new AtomicInteger(-1);
        }
    };

    /**
     * 用 数组来模拟栈来实现 延迟出栈
     */
    private static final ThreadLocal<ArrayList<MonitoringData>> TEMPORARY_STACK = new ThreadLocal() {
        @Override
        protected Object initialValue() {
            return new ArrayList<MonitoringData>();
        }
    };


    /**
     * 记录方法入栈的时间
     */
    private static final ThreadLocal<Stack<Instant>> ADD_STACK_INSTANT = new ThreadLocal() {
        @Override
        protected Object initialValue() {
            return new Stack<Instant>();
        }
    };

    /**
     * 入栈出栈的回调
     */
    private static AbstractCallback callback = new AbstractCallback() {
        @Override
        public void start(MonitoringData monitoringData) {

        }

        @Override
        public void end(MonitoringData monitoringData) {

        }


    };

    private static class MonitoringDataTree {
        MonitoringData node;
        List<MonitoringDataTree> childNode = new ArrayList<MonitoringDataTree>();

        public MonitoringData getNode() {
            return node;
        }

        public void setNode(MonitoringData node) {
            this.node = node;
        }

        public List<MonitoringDataTree> getChildNode() {
            return childNode;
        }

        public void setChildNode(List<MonitoringDataTree> childNode) {
            this.childNode = childNode;
        }
    }


    public static class MonitoringData {

        public MonitoringData() {

        }

        public MonitoringData(String id, String belongGroup, String describe, boolean isGroup, boolean finishedState, String finishedTime, String timeConsuming) {
            this.id = id;
            this.belongGroup = belongGroup;
            this.describe = describe;
            this.isGroup = isGroup;
            this.finishedState = finishedState;
            this.finishedTime = finishedTime;
            this.timeConsuming = timeConsuming;
        }

        private String id;
        private String belongGroup;
        private String describe;

        private boolean isGroup;
        /**
         * 完成状态
         */
        private boolean finishedState;

        /**
         * 完成时间
         */
        private String finishedTime;

        /**
         * 耗时
         */
        private String timeConsuming;

        public boolean isGroup() {
            return isGroup;
        }

        public void setGroup(boolean group) {
            isGroup = group;
        }

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public String getBelongGroup() {
            return belongGroup;
        }

        public void setBelongGroup(String belongGroup) {
            this.belongGroup = belongGroup;
        }

        public String getTimeConsuming() {
            return timeConsuming;
        }

        public void setTimeConsuming(String timeConsuming) {
            this.timeConsuming = timeConsuming;
        }

        public String getDescribe() {
            return describe;
        }

        public void setDescribe(String describe) {
            this.describe = describe;
        }

        public boolean isFinishedState() {
            return finishedState;
        }

        public void setFinishedState(boolean finishedState) {
            this.finishedState = finishedState;
        }

        public String getFinishedTime() {
            return finishedTime;
        }

        public void setFinishedTime(String finishedTime) {
            this.finishedTime = finishedTime;
        }

    }


    public static abstract class AbstractCallback {
        /**
         * -@Oversee方法开始执行 回调
         *
         * @param monitoringData
         */
        public abstract void start(MonitoringData monitoringData);

        /**
         * -@Oversee方法结束执行 回调
         *
         * @param monitoringData
         */
        public abstract void end(MonitoringData monitoringData);


    }


    /**
     * 用户自定义 实现 回调后应该干什么
     *
     * @param callback
     */
    public static void setCallback(AbstractCallback callback) {
        MonitoringCache.callback = callback;
    }


    private static List<MonitoringDataTree> createTree(String belongGroup, List<MonitoringDataTree> trees, Set<Map.Entry<String, MonitoringData>> entries) {
        List<MonitoringDataTree> treeMenu = new ArrayList();
        for (Map.Entry<String, MonitoringData> entry : entries) {
            MonitoringData value = entry.getValue();
            MonitoringDataTree monitoringDataTree = new MonitoringDataTree();
            if (belongGroup.equals(value.getBelongGroup())) {
                monitoringDataTree.setNode(value);
                monitoringDataTree.setChildNode(createTree(value.getId(), treeMenu, entries));
                treeMenu.add(monitoringDataTree);
                continue;
            }
        }
        return treeMenu;
    }

    /**
     * 将Cache 转化为Tree结构返回 会将当前Tree缓存
     * 如果Tree缓存不为空 则直接返回Tree缓存
     *
     * @return
     */
    public static synchronized List getDataCacheTree() {
        if (MonitoringDataCacheTree == null || MonitoringDataCacheTree.isEmpty()) {
            Set<Map.Entry<String, MonitoringData>> entries = MONITORING_DATA_CACHE.entrySet();
            MonitoringDataCacheTree = createTree("", null, entries);
        }
        return MonitoringDataCacheTree;
    }

    /**
     * 强制刷新 树结构
     */
    public static void refreshDataCacheTree() {
        if (MonitoringDataCacheTree != null && !MonitoringDataCacheTree.isEmpty()) {
            Set<Map.Entry<String, MonitoringData>> entries = MONITORING_DATA_CACHE.entrySet();
            MonitoringDataCacheTree = createTree("", null, entries);
        }
    }


    /**
     * MonitoringData 入栈
     * <p>
     * 当前 MonitoringData 是group 且id不为空时 可已从MonitoringDataCache获取
     */
    public static void addStack(MonitoringData obj) {
        String id = obj.getId();
        if (StringUtil.isNotEmpty(id) && obj.isGroup()) {
            MonitoringData monitoringData = MONITORING_DATA_CACHE.get(id);
            if (Objects.nonNull(monitoringData)) {
                monitoringData.setFinishedState(false);
                monitoringData.setTimeConsuming("");
                obj = monitoringData;
            }
        }
        callback.start(obj);
        ArrayList arrayList = TEMPORARY_STACK.get();
        arrayList.add(obj);
        INDEX.get().incrementAndGet();
        Stack<Instant> instants = ADD_STACK_INSTANT.get();
        instants.push(Instant.now());
    }

    /**
     * 更新 栈顶数据
     * 因为很多操作 是通过 @Oversee的   getXXXSteps() 来动态获取的
     *
     * @param id
     * @param describe
     */
    public static void updateTopOfStack(String id, String describe) {

        Integer i = INDEX.get().get();
        ArrayList<MonitoringData> arrayList = TEMPORARY_STACK.get();
        MonitoringData o = arrayList.get(i);
        if (id != null && id != "") {
            o.setId(id);
        }
        if (describe != null && describe != "") {
            o.setDescribe(describe);
        }

    }


    /**
     * 重新定义 组内 isGroup为false 的id 为  belongGroup(id)
     *
     * @param id
     * @param belongGroup
     * @return belongGroup(id)
     */
    private static String definitionKey(String id, String belongGroup) {
        StringBuilder key = new StringBuilder();
        key.append(belongGroup);
        key.append("-");
        key.append(id);
        return key.toString();
    }


    public static void popStack() {
        int i = INDEX.get().get();
        if (i <= -1) {
            return;
        }
        ArrayList<MonitoringData> arrayList = TEMPORARY_STACK.get();
        MonitoringData o = arrayList.get(i);
        String id = o.getId();
        boolean group = o.isGroup();
        String belongGroup = o.getBelongGroup();
        boolean belongGroupNotEmpty = StringUtil.isNotEmpty(belongGroup);
        Stack<Instant> instants = ADD_STACK_INSTANT.get();
        Instant pop = instants.pop();
        o.setFinishedState(true);
        o.setTimeConsuming(String.valueOf(timeConsuming(pop, Instant.now())));

        if (group) {
            int size = arrayList.size()-1;
            int stackSize = i + 1;
            if (size >= stackSize) {
                for (int i1 = stackSize; i1 <= size; i1++) {
                    MonitoringData remove = arrayList.remove(stackSize);
                    String definitionKey = remove.getId();
                    if(!remove.isGroup){
                        definitionKey = definitionKey(remove.getId(), id);
                        remove.setId(definitionKey);
                    }
                    remove.setBelongGroup(id);
                    MONITORING_DATA_CACHE.put(definitionKey, remove);
                    callback.end(remove);
                }
            }
        }
        if (belongGroupNotEmpty||i==0) {
            arrayList.remove(i);
            MONITORING_DATA_CACHE.putIfAbsent(id, o);
        }
        if(group||belongGroupNotEmpty){
            callback.end(o);
        }
        INDEX.get().decrementAndGet();
    }

    public static Long timeConsuming(Instant start, Instant end) {
        Duration timeElapsed = Duration.between(start, end);
        return timeElapsed.toMillis();
    }

}
